/**
 * @(#)SimulatorListenServiceImpl.java 2017年2月18日
 * Copyright(C) 2017 ZHEXIN IT CO.,LTD. All rights reserved.
 */
package org.jsbd.boss.service.impl;

import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;

import org.apache.commons.lang.StringUtils;
import org.jsbd.boss.common.redis.RedisClient;
import org.jsbd.boss.domian.CabinetConfigBean;
import org.jsbd.boss.domian.channel.ClientHeartBeat;
import org.jsbd.boss.domian.channel.ClientRunInfo;
import org.jsbd.boss.domian.channel.ClientTaskInfo;
import org.jsbd.boss.domian.channel.LocationInfo;
import org.jsbd.boss.domian.channel.SimulatorTaskResult;
import org.jsbd.boss.service.ICabinetConfigService;
import org.jsbd.boss.service.ISimulatorListenService;
import org.jsbd.boss.service.ISimulatorTaskService;
import org.jsbd.boss.util.CacheKey;
import org.jsbd.boss.util.DateUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import edu.hziee.common.queue.DelayExecuteBuffer;

/**
 * 模拟器监听
 * 
 * @author <a href="mailto:zhouchao@xingbook.com" >周超</a>
 * @version 1.0.0
 */
@Service("simulatorListenService")
public class SimulatorListenServiceImpl implements ISimulatorListenService {
	public static final Logger LOGGER = LoggerFactory.getLogger(SimulatorListenServiceImpl.class);

	/**
	 * 在线有效时间（120秒），单位毫秒
	 */
	private static final long ONLINE_TIME = 2 * 60 * 1000;

	@Autowired
	private RedisClient<String, Object> redisClient;
	@Autowired
	private ICabinetConfigService cabinetConfigService;
	@Autowired
	private ISimulatorTaskService simulatorTaskService;
	@Autowired
	private DelayExecuteBuffer<SimulatorTaskResult> simulatorTaskResultBuffer;

	@Override
	public void updateSimulatorStatus(ClientHeartBeat heartBeat) {
		if (heartBeat.hasStop()) {
			removeTask(heartBeat.getUuid() + heartBeat.getClientMac());
		}

		heartBeat.setCurrentTime(System.currentTimeMillis());
		redisClient.putHash(CacheKey.SIMULATOR_HEART_HASH_KEY, heartBeat.getUuid() + heartBeat.getClientMac(), heartBeat);
		LOGGER.info("heartBeat is [{}]", heartBeat);
	}

	@Override
	public ClientTaskInfo getTask(ClientHeartBeat heartBeat) {
		// 判断模拟器是否在执行任务，没有执行则去获取任务
		ClientTaskInfo taskInfo = null;

		// 如果任务执行完了，更新数据库日志
		if (heartBeat.hasStop()) {
			updateTaskUpdate(heartBeat);
		}

		// 如果是模拟器是空闲状态，则试着获取一个任务
		if (!heartBeat.hasBusy()) {
			taskInfo = (ClientTaskInfo) redisClient.getHash(CacheKey.SIMULATOR_TASK_HASH_KEY, heartBeat.getUuid() + heartBeat.getClientMac());
			if (taskInfo != null) {
				redisClient.removeHash(CacheKey.SIMULATOR_TASK_HASH_KEY, heartBeat.getUuid() + heartBeat.getClientMac());
				updateTaskUpdate(taskInfo, heartBeat.getUuid());
				heartBeat.setTaskId(taskInfo.getTaskId());
			}
		}

		// 如果模拟器获取到了任务，则执行
		if (taskInfo != null) {
			heartBeat.startWork();
		}

		// 更新模拟器状态
		updateSimulatorStatus(heartBeat);

		if (taskInfo == null) {
			taskInfo = new ClientTaskInfo();
		}

		LOGGER.info("taskInfo is [{}]", taskInfo);

		return taskInfo;
	}

	@Override
	public List<ClientRunInfo> getSimulatorList(ClientRunInfo condition) {

		LOGGER.info("condition is [{}]", condition);

		List<ClientRunInfo> runInfoList = new ArrayList<ClientRunInfo>();

		Map<Object, Object> dataMap = redisClient.getHash(CacheKey.SIMULATOR_HEART_HASH_KEY);
		// 获取设备对应的任务
		Map<Object, Object> taskMap = redisClient.getHash(CacheKey.SIMULATOR_TASK_HASH_KEY);

		Map<String, String> locationMap = getLocation();

		for (Entry<Object, Object> entity : dataMap.entrySet()) {
			ClientRunInfo runInfo = new ClientRunInfo();
			ClientHeartBeat heart = (ClientHeartBeat) entity.getValue();
			runInfo.setActionType(heart.getActionType());
			runInfo.setClientMac(heart.getClientMac());

			ClientTaskInfo task = (ClientTaskInfo) taskMap.get(entity.getKey());
			if (task != null) {
				runInfo.setTaskId(task.getTaskId());
				if (!runInfo.hasBusy()) {
					runInfo.waitting();
				}
			}

			runInfo.setClientIp(heart.getClientIp());
			if (heart.getCurrentTime() + ONLINE_TIME > System.currentTimeMillis()) {
				runInfo.setOnline(ClientRunInfo.ON_LINE);
			} else {
				runInfo.setOnline(ClientRunInfo.OFF_LINE);
			}

			addLocation(locationMap, runInfo);

			runInfo.setStatus(heart.getStatus());
			if (StringUtils.isBlank(runInfo.getTaskId())) {
				runInfo.setTaskId(heart.getTaskId());
			}
			runInfo.setUuid(heart.getUuid());
			runInfo.setVersion(heart.getVersion());
			runInfo.setOsVersion(heart.getOsVersion());
			runInfo.setHeartVersion(heart.getHeartVersion());
			runInfo.setCurrentTime(DateUtil.getYmdhmsDateStr(new Date(heart.getCurrentTime())));
			if (validateCondition(condition, runInfo)) {
				runInfoList.add(runInfo);
			}
		}

		// 对机会机型查询
		if (StringUtils.isNotBlank(condition.getLocation())) {
			Iterator<ClientRunInfo> it = runInfoList.iterator();
			while (it.hasNext()) {
				if (!it.next().getLocation().contains(condition.getLocation())) {
					it.remove();
				}
			}
		}

		return runInfoList;
	}

	/**
	 * 
	 * 添加模拟器信息
	 * 
	 * @author <a href="mailto:zhouchao@xingbook.com" >周超</a>
	 * @param runInfoList
	 */
	private void addLocation(Map<String, String> locationMap, ClientRunInfo clientInfo) {
		String clientIp = clientInfo.getClientIp();
		if (StringUtils.isBlank(clientIp)) {
			clientInfo.setLocation("未知");
			return;
		}

		int lastIdx = clientIp.lastIndexOf(".");
		if (lastIdx > 0) {
			clientIp = clientIp.substring(0, lastIdx);
		}

		String location = locationMap.get(clientIp);
		if (StringUtils.isBlank(location)) {
			location = "未知";
		}
		clientInfo.setLocation(location);
	}

	/**
	 * 
	 * 获取机柜信息
	 * 
	 * @author <a href="mailto:zhouchao@zhexinit.com" >周超</a>
	 * @return
	 */
	private Map<String, String> getLocation() {
		Map<String, String> locationMap = (Map<String, String>) redisClient.get(CacheKey.SIMULATOR_LOCATION_MAP_KEY);
		if (locationMap == null) {
			locationMap = new HashMap<String, String>();
			List<CabinetConfigBean> configList = cabinetConfigService.getCabinetConfigList(new CabinetConfigBean());
			for (CabinetConfigBean cabinet : configList) {
				locationMap.put(cabinet.getIpSection(), cabinet.getLocation());
			}
			redisClient.put(CacheKey.SIMULATOR_LOCATION_MAP_KEY, locationMap);
		}
		return locationMap;
	}

	private boolean validateCondition(ClientRunInfo condition, ClientRunInfo runInfo) {
		if (condition.getActionType() != null) {
			if (!condition.getActionType().equals(runInfo.getActionType())) {
				return false;
			}
		}

		if (condition.getOnline() != null) {
			if (!condition.getOnline().equals(runInfo.getOnline())) {
				return false;
			}
		}

		if (StringUtils.isNotBlank(condition.getClientIp())) {
			if (StringUtils.isBlank(runInfo.getClientIp())) {
				return false;
			}
			if (!runInfo.getClientIp().contains(condition.getClientIp())) {
				return false;
			}
		}

		if (StringUtils.isNotBlank(condition.getClientMac())) {
			if (StringUtils.isBlank(runInfo.getClientMac())) {
				return false;
			}
			if (!runInfo.getClientMac().contains(condition.getClientMac())) {
				return false;
			}
		}

		if (StringUtils.isNotBlank(condition.getUuid())) {
			if (StringUtils.isBlank(runInfo.getUuid())) {
				return false;
			}

			if (!runInfo.getUuid().contains(condition.getUuid())) {
				return false;
			}
		}

		if (StringUtils.isNotBlank(condition.getVersion())) {

			if (StringUtils.isBlank(runInfo.getVersion())) {
				return false;
			}

			if (!runInfo.getVersion().equals(condition.getVersion())) {
				return false;
			}
		}

		if (StringUtils.isNotBlank(condition.getHeartVersion())) {

			if (StringUtils.isBlank(runInfo.getHeartVersion())) {
				return false;
			}

			if (!runInfo.getHeartVersion().equals(condition.getHeartVersion())) {
				return false;
			}
		}

		if (StringUtils.isNotBlank(condition.getOsVersion())) {

			if (StringUtils.isBlank(runInfo.getOsVersion())) {
				return false;
			}

			if (!runInfo.getOsVersion().equals(condition.getOsVersion())) {
				return false;
			}
		}

		return true;
	}

	@Override
	public void addTask(Map<String, Object> taskMap) {
		redisClient.putHashNoExpire(CacheKey.SIMULATOR_TASK_HASH_KEY, taskMap);
		simulatorTaskService.saveSimulatorTaskAndResult(taskMap);
	}

	@Override
	public void removeTask(Object... uuid) {
		redisClient.removeHash(CacheKey.SIMULATOR_TASK_HASH_KEY, uuid);
	}

	@Override
	public boolean hasTask(List<Object> keyList) {
		List<Object> taskList = redisClient.getHash(CacheKey.SIMULATOR_TASK_HASH_KEY, keyList);
		if (taskList == null || taskList.isEmpty()) {
			return false;
		}

		for (Object task : taskList) {
			if (task != null) {
				return true;
			}
		}

		return false;
	}

	@Override
	public void clearLocation() {
		redisClient.remove(CacheKey.SIMULATOR_LOCATION_MAP_KEY);
	}

	/**
	 * 
	 * 更新数据库中任务的状态
	 * 
	 * @author <a href="mailto:zhouchao@xingbook.com" >周超</a>
	 * @param heartBeat
	 */
	private void updateTaskUpdate(ClientHeartBeat heartBeat) {
		SimulatorTaskResult taskResult = new SimulatorTaskResult();
		if (StringUtils.isBlank(heartBeat.getTaskId())) {
			LOGGER.error("taskId is null,heartBean={}", heartBeat);
			return;
		}
		taskResult.setUuid(heartBeat.getUuid());
		taskResult.setTaskId(heartBeat.getTaskId());
		taskResult.setTaskResult(heartBeat.getMsg());
		taskResult.setTaskStatus(heartBeat.getActionType());
		simulatorTaskResultBuffer.add(taskResult);
	}

	/**
	 * 
	 * 更新数据库中任务的状态
	 * 
	 * @author <a href="mailto:zhouchao@xingbook.com" >周超</a>
	 * @param taskInfo
	 */
	private void updateTaskUpdate(ClientTaskInfo taskInfo, String uuid) {
		SimulatorTaskResult taskResult = new SimulatorTaskResult();
		if (StringUtils.isBlank(taskInfo.getTaskId())) {
			LOGGER.error("taskId is null,taskInfo={}", taskInfo);
			return;
		}
		taskResult.setTaskId(taskInfo.getTaskId());
		taskResult.setTaskStatus(ClientHeartBeat.RUNNING);
		taskResult.setUuid(uuid);
		simulatorTaskResultBuffer.add(taskResult);
	}

	/**
	 * 清理离线模拟器
	 */
	@Override
	public void clearOffline(String uuids) {
		redisClient.removeHash(CacheKey.SIMULATOR_HEART_HASH_KEY, uuids.split(","));
	}

	@Override
	public List<LocationInfo> getLocationNumList(LocationInfo locationInfo) {

		List<LocationInfo> liList = new ArrayList<LocationInfo>();

		// 获取设备
		Map<Object, Object> dataMap = redisClient.getHash(CacheKey.SIMULATOR_HEART_HASH_KEY);

		// 机柜
		Map<String, String> locationMap = getLocation();

		Map<String, Integer> map = new HashMap<String, Integer>();
		for (Entry<Object, Object> entity : dataMap.entrySet()) {
			ClientHeartBeat heart = (ClientHeartBeat) entity.getValue();

			String clientIp = heart.getClientIp();
			int lastIdx = clientIp.lastIndexOf(".");
			if (lastIdx > 0) {
				clientIp = clientIp.substring(0, lastIdx);
			}

			String key = "";
			if (heart.getCurrentTime() + ONLINE_TIME > System.currentTimeMillis()) {
				key = clientIp + "_ONLINE";
			} else {
				key = clientIp + "_OFFLINE";
			}
			Integer v = map.get(key);
			if (null != v) {
				map.put(key, ++v);
			} else {
				map.put(key, 1);
			}

		}

		for (Entry<String, String> entity : locationMap.entrySet()) {
			LocationInfo li = new LocationInfo();

			String key = entity.getKey();

			li.setLocation(entity.getValue());
			li.setOnNum(map.get(key + "_ONLINE"));
			li.setOffNum(map.get(key + "_OFFLINE"));

			liList.add(li);
		}

		return liList;
	}

	@Override
	public List<ClientRunInfo> getMacRepeatList(ClientRunInfo condition) {
		LOGGER.info("condition is [{}]",condition);
		
		List<ClientRunInfo> runInfoList = new ArrayList<ClientRunInfo>();
		
		Map<Object,Object> dataMap = redisClient.getHash(CacheKey.SIMULATOR_HEART_HASH_KEY);
		
		Map<String,String> locationMap = getLocation();
		
		Map<String, Integer> macs = new HashMap<String, Integer>(); 
		Map<String, Object> mapRepeat = new HashMap<String, Object>();//记录重复mac
		
		for (Entry<Object, Object> entity :dataMap.entrySet()){
			ClientRunInfo runInfo=new ClientRunInfo();
			ClientHeartBeat heart = (ClientHeartBeat)entity.getValue();
			
			String mac = heart.getClientMac();
			Integer value = macs.get(mac);
			if (null == value) {
				macs.put(mac, 1);
				mapRepeat.put(mac, heart);
			} else {
				Integer i = ++value;
				if (i == 2) {
					ClientHeartBeat temp_heart = (ClientHeartBeat) mapRepeat.get(mac);
					ClientRunInfo temp_runInfo = new ClientRunInfo();
					
					setSimulatorInfo(temp_runInfo, temp_heart, locationMap);
					runInfoList.add(temp_runInfo);
				}
				macs.put(mac, i);
				
				setSimulatorInfo(runInfo, heart, locationMap);
				runInfoList.add(runInfo);
			}
		}
		
		return runInfoList;
	}
	
	private void setSimulatorInfo(ClientRunInfo runInfo, ClientHeartBeat heart, Map<String,String> locationMap) {
		runInfo.setActionType(heart.getActionType());
		runInfo.setClientMac(heart.getClientMac());
		runInfo.setClientIp(heart.getClientIp());
		addLocation(locationMap,runInfo);
		runInfo.setUuid(heart.getUuid());
		runInfo.setVersion(heart.getVersion());
		runInfo.setOsVersion(heart.getOsVersion());
		runInfo.setHeartVersion(heart.getHeartVersion());
		runInfo.setCurrentTime(DateUtil.getYmdhmsDateStr(new Date(heart.getCurrentTime())));
	}
	
	
	
}
